괴담집 프로젝트에서 Howler.js 사용하기

작성일:2025. 7. 8.
수정일:2025. 7. 10.
howler landing
howler landing

Howler는 웹에서의 오디오 제어를 위한 라이브러리로 좋은 성능에 다양한 기능을 지원한다. 특히 괴담집 프로젝트에 필요한 fade효과나 loop효과 그리고 여러 소리에 대한 개별 제어등 딱 알맞는 라이브러리라고 생각된다.

이후에 업데이트할 때에는 공간음향같은 Howler.js에서 제공하는 특수효과도 사용하여 속삭임같은 무서운 음향효과를 구현할수도 있을것 같다는 생각이다.

기본 사용법#

Typescripttypescript

howler는 기본적으로 두가지 객체를 통해 소리를 로드하고 제어한다.

  • Howl: Howl은 개별 사운드에 대한 객체이며 해당 객체를 통해 각각의 소리를 제어할 수 있다.
  • Howler: Howler는 글로벌 제어를 위한 객체이며 전체 볼륨제어와 같은 전체 사운드에 대한 제어를 가능하게 한다.

React에서의 사용#

React TSXtsx

에셋을 불러오는 작업과 React의 리렌더링은 수많은 메모리 누수를 일으킬 수 있다. 따라서 사운드를 불러오기 위해선 useRef를 활용하는것이 가장 좋다.

useRef를 사용하지 않았을때의 문제#

❌ useRef 없이 일반 변수 사용:#

React TSXtsx

문제 시나리오:

  1. "재생" 클릭 → 소리 재생 시작
  2. setCount → 컴포넌트 리렌더링
  3. let sound = null 다시 실행 → 이전 Howl 인스턴스 참조 잃음
  4. "정지" 클릭 → sound는 null이라 에러!
  5. 오디오는 백그라운드에서 계속 재생 (제어 불가)

❌ useState 사용:#

React TSXtsx
  1. UI변경이 없어 리렌더링이 불필요 한데도 소리의 변경 때문에 리렌더링이 발생할 수 있음.
  2. UI와 무관한 값은 ref에 저장한다는 철학에 위배됨ㄴ

✅ useRef 사용 (올바른 방법):#

React TSXtsx

본격적인 사용을 위한 고민#

Howler.js를 잘 사용하기 위해서 그리고 앱에서 아무 문제 없도록 사용하기 위해서 React와 관련된 몇가지 문제를 생각해 보아야 한다.

  1. 인스턴스 문제
  2. 사운드의 캐싱 문제
  3. 중복 로딩 문제

리엑트의 생명주기와의 충돌때문에 보편적으로 위와 같은 문제가 발생할 수 있다. howler 인스턴스에 대한 제어권을 잃어버릴수 있고 메모리 누수로 이어질 수 있으며 이미 캐싱이 되어있는데 다시 시도할수도 있고 여러가지 문제가 발생할 수 있다.

따라서 Class형식을 통해 싱글톤 패턴을 구현하여 추상화하고 추상화된 모듈을 훅을 통하여 접근할 수 있도록 하는게 좋을것이라고 판단된다.

AudioManager 만들기#

Typescripttypescript

캐싱 문제와 인스턴스 문제를 해결하기 위해 싱글톤 패턴의 클래스를 하나 만든다. 클래스를 통해서 관련 기능들을 하나로 묶을 수 있고, private을 통해 보호할 수 있다.

캐싱 시스템 구현하기#

Typescripttypescript

오디오의 캐싱과 중복 로딩을 방지하기 위해 private으로 map자료구조의 변수를 선언해준다.

프리로드 기능#

Typescripttypescript

넘겨받을 에셋의 타입을 정의하고 해당 정보를 등록한다. 이때 존재한다면 이미 존재하는걸 반환해야 한다.

로직의 큰 구조는 먼저 캐시 혹은 로딩중인것 즉 메모리를 먼저 확인하여 현재 작업해야하는 에셋과 비교한 이후에 있다면 현재 저장된 데이터를 반환하고 그렇지 않다면 loadAudioAsset을 활용해 howl 객체를 만든다. 해당 객체가 load 되면 onload메소드를 통해 Promise의 결과값을 위한 resolve({howl,asset,loadedAt})을 반환한다.

제어 기능들#

Typescripttypescript

이제 기본적인 제어에 대한 구현은 완료 되었다. 실제로 이 모듈이 어떻게 사용될지는 구조를 조금 더 살펴보면서 정해야 할것으로 보인다.